package com.jeasonzhao.report.model;

import com.jeasonzhao.commons.json.JSONArray;
import com.jeasonzhao.commons.json.JSONClass;
import com.jeasonzhao.commons.json.JSONNode;
import com.jeasonzhao.commons.utils.Algorithms;
import com.jeasonzhao.commons.utils.DataTypes;
import com.jeasonzhao.commons.xml.XMLNode;
import com.jeasonzhao.model.Model;
import com.jeasonzhao.model.ModelException;
import com.jeasonzhao.model.ModelField;
import com.jeasonzhao.model.ModelHelper;
import com.jeasonzhao.report.codes.ColumnRotateHeaderModes;
import com.jeasonzhao.report.codes.HorizonAligns;
import com.jeasonzhao.report.codes.ReportAlertModels;
import com.jeasonzhao.report.codes.ReportColumnTypes;
import com.jeasonzhao.report.codes.ReportMergeModes;
import com.jeasonzhao.report.codes.ReportSortModes;

/**
 * Ugly model, if you add some new properties into this class,
 * remember that you should add some code into the "copy construct"
 * to make sure those properties effect.
 * I do not use the default copy construct, because it it not safe enough.
 */
@Model("column")
public class ReportColumn extends BaseReportModelItem
{
	private static final long serialVersionUID=1L;
    private static final String S_MEASURE = "isMeasure,measure";
    private static final String S_ALIGN = "align";
    private static final String S_SUMMARYABLE = "summaryable";
    public static final String NODE_ALERTS = "alert";

    @ModelField(names = "reportId")
    private String m_strReportId = null;

    @ModelField(names = "name,title")
    private String m_strTitle = null;

    @ModelField(names = "sortNumber")
    private int m_nSortNumber = -1;

    @ModelField(names = "field,column,col")
    private String m_strFieldName = null;

    @ModelField(names = "dataType")
    private DataTypes m_dataType = DataTypes.STRING;

    @ModelField(names = "columnType,colType")
    private ReportColumnTypes m_columnType = ReportColumnTypes.Normal;

    @ModelField(names = "mergeMode,merge")
    private ReportMergeModes m_MergeMode = ReportMergeModes.None;

    @ModelField(names = "mergeTitle")
    private String m_strMergeTitle = null;

    @ModelField(names = "dataFormat,format")
    private String m_strDataFormat = null;

    @ModelField(names = "dictId,dict")
    private String m_strDictId = null;

    @ModelField(names = "rotateMode,RotateMode,rotate")
    private ColumnRotateHeaderModes m_rotateMode = ColumnRotateHeaderModes.None;

    @ModelField(names = "rotateGroupNo,rotateIndex,rotateNum")
    private int m_nRotateGroupNo = -1;

    @ModelField(names = "isHide,hidden,hide,ishide,ishidden")
    private boolean m_isHidden = false;

    @ModelField(names = "notes,comment,comnments,description",cdata = true)
    private String m_strNotes = null;

    @ModelField(names = S_ALIGN)
    private HorizonAligns m_align = HorizonAligns.Left;

    @ModelField(names = S_MEASURE)
    private boolean m_isMeasure = false;

    @ModelField(names = S_SUMMARYABLE) //isCalculability
    private boolean m_summaryable = false;

    @ModelField(names = "color")
    private String m_strColor = null;

    @ModelField(names = "bgColor,backgroundcolor,background")
    private String m_strBgColor = null;

    @ModelField(names = "expression,calcScript")
    private String m_strCalcScript = null;

    @ModelField(names = "totalScript")
    private String m_strTotalScript = null;

    @ModelField(names = "sortScript")
    private String m_strSortScript = null;

    @ModelField(names = "sortMode,sort")
    private ReportSortModes m_sortModes = ReportSortModes.None;

    @ModelField(names = "width")
    private int m_nWidth = -1;

    @ModelField(collection = true,names = NODE_ALERTS)
    private ReportAlertCollection m_alerts = null;

    //////////////////////////////////////////////////////////////////////
    //Dynamic values
    private String m_strFilterScript = null; //Filter script
    private ReportColumnValueCollection m_verticalKeys = null; //Rotated keys

    public ReportColumn()
    {
    }

    public ReportColumn(String strFieldName)
    {
        super();
        this.m_strTitle = strFieldName;
        this.m_strFieldName = strFieldName;
    }

    public ReportColumn(String strFieldName,DataTypes dataType)
    {
        super();
        this.m_strTitle = strFieldName;
        this.m_dataType = dataType;
        this.m_strFieldName = strFieldName;
        if(null != dataType)
        {
            this.setMeasure(dataType.isNumeric());
        }
    }

    public ReportColumn(ReportColumn col)
    {
        super(col);
        copyFrom(col);
    }

    //Normal Field:    Field:Name@Type
    //Calculating Field: {Calc}:Name@Type
    public static ReportColumn createByMagicString(String strMagic)
    {
        if(Algorithms.isEmpty(strMagic))
        {
            return null;
        }
        String strFieldName = null;
        String strTitle = null;
        String strType = null;
        boolean isExpression = false;
        int n = strMagic.lastIndexOf("@");
        if(n >= 0)
        {
            strType = strMagic.substring(n + 1);
            strMagic = strMagic.substring(0,n);
        }
        n = strMagic.lastIndexOf(":");
        if(n >= 0)
        {
            strFieldName = strMagic.substring(0,n);
            strTitle = strMagic.substring(n + 1);
        }
        else
        {
            strFieldName = strMagic;
        }
        if(Algorithms.isEmpty(strFieldName) && Algorithms.isEmpty(strTitle) == false)
        {
            strFieldName = strTitle;
        }
        if(Algorithms.isEmpty(strFieldName))
        {
            return null;
        }
        strFieldName = strFieldName.trim();
        if(Algorithms.isEmpty(strTitle))
        {
            strTitle = strFieldName;
        }
        isExpression = strFieldName.startsWith("{") && strFieldName.endsWith("}");
        if(isExpression)
        {
            strFieldName = strFieldName.substring(1,strFieldName.length() - 1);
        }
        ReportColumn col = new ReportColumn();
        if(isExpression)
        {
            col.setCalcScript(strFieldName);
            col.setColumnType(ReportColumnTypes.CalcColumn);
        }
        else
        {
            col.setFieldName(strFieldName);
            col.setColumnType(ReportColumnTypes.Normal);
        }
        col.setTitle(strTitle);
        if(Algorithms.notEmpty(strType))
        {
            DataTypes dt = DataTypes.from(strType);
            if(dt.equals(DataTypes.OBJECT) == false)
            {
                col.setDataType(dt);
            }
        }
        return col;
    }

    public ReportColumnValueCollection getVerticalKeys()
    {
        return m_verticalKeys;
    }

    public ReportColumn setVerticalKeys(ReportColumnValueCollection k)
    {
        m_verticalKeys = k;
        return this;
    }

    public void copyFrom(ReportColumn col)
    {
        if(null == col)
        {
            return;
        }
        super.copyFrom(col);
        this.m_strReportId = col.m_strReportId;
        this.m_nSortNumber = col.m_nSortNumber;
        this.m_strFieldName = col.m_strFieldName;
        this.m_strTitle = col.m_strTitle;
        this.m_columnType = col.m_columnType;
        this.m_dataType = col.m_dataType;
        this.m_strDataFormat = col.m_strDataFormat;
        this.m_strDictId = col.m_strDictId;
        this.m_rotateMode = col.m_rotateMode;
        this.m_nRotateGroupNo = col.m_nRotateGroupNo;
        this.m_isHidden = col.m_isHidden;
        this.m_strNotes = col.m_strNotes;
        this.m_align = col.m_align;
        this.m_isMeasure = col.m_isMeasure;
        this.m_strColor = col.m_strColor;
        this.m_strBgColor = col.m_strBgColor;
        this.m_strCalcScript = col.m_strCalcScript;
        this.m_MergeMode = col.m_MergeMode;
        this.m_strMergeTitle = col.m_strMergeTitle;
        this.m_summaryable = col.m_summaryable;
        this.m_sortModes = col.m_sortModes;
        this.m_strTotalScript = col.m_strTotalScript;
        this.m_strSortScript = col.m_strSortScript;
        this.m_nWidth = col.m_nWidth;
        this.m_strFilterScript = col.m_strFilterScript;
        m_alerts = ReportAlertCollection.from(col.getAlerts());
        this.m_verticalKeys = col.m_verticalKeys;
    }

    public ReportColumn setFilterScript(String str)
    {
        m_strFilterScript = str;
        return this;
    }

    public String getFilerScript()
    {
        return m_strFilterScript == null || m_strFilterScript.trim().length() < 1 ? null : m_strFilterScript.trim();
    }

    public ReportMergeModes getMergeMode()
    {
        return m_MergeMode == null ? ReportMergeModes.None : m_MergeMode;
    }

    public ReportColumn setMergeMode(ReportMergeModes groupMode)
    {
        this.m_MergeMode = groupMode;
        return this;
    }

    public String getCalcScript()
    {
        return m_strCalcScript;
    }

    public ReportColumn addAlerts(ReportAlert ...cls)
    {
        for(int n = 0;null != cls && n < cls.length;n++)
        {
            if(null != cls[n])
            {
                if(null == m_alerts)
                {
                    m_alerts = new ReportAlertCollection();
                }
                m_alerts.add(cls[n]);
            }
        }
        return this;
    }

    public ReportColumn setAlerts(ReportAlert ...cls)
    {
        m_alerts = null;
        return addAlerts(cls);
    }

    public ReportColumn setAlerts(ReportAlertCollection cls)
    {
        m_alerts = cls;
        return this;
    }

    public ReportAlertCollection getAlerts()
    {
        return m_alerts;
    }

    public int getColId()
    {
        return m_nSortNumber;
    }

    public String getColor()
    {
        return m_strColor;
    }

    public String getSortScript()
    {
        return m_strSortScript == null || m_strSortScript.trim().length() < 1 ? null : m_strSortScript;
    }

    public ReportColumn setSortScript(String str)
    {
        m_strSortScript = str;
        return this;
    }

    public String getTotalScript()
    {
        return m_strTotalScript == null || m_strTotalScript.trim().length() < 1 ? null : m_strTotalScript;
    }

    public ReportColumn setTotalScript(String str)
    {
        m_strTotalScript = str;
        return this;
    }

    public String getBgColor()
    {
        return m_strBgColor;
    }

    public ReportColumnTypes getColumnType()
    {
        return m_columnType == null ? ReportColumnTypes.Normal : m_columnType;
    }

    public String getDictId()
    {
        return m_strDictId == null || m_strDictId.trim().length() < 1 ? null : m_strDictId;
    }

    public String getFieldName()
    {
        return m_strFieldName;
    }

    public String getDataFormat()
    {
        return m_strDataFormat;
    }

    public HorizonAligns getHorizonAlign()
    {
        return m_align == null ? HorizonAligns.Left : m_align;
    }

    public int getWidth()
    {
        return m_nWidth;
    }

    public ReportSortModes getSortMode()
    {
        return m_sortModes == null ? ReportSortModes.None : m_sortModes;
    }

    public boolean isHidden()
    {
        return m_isHidden;
    }

    public boolean isDimension()
    {
        return false == isMeasure();
    }

    public boolean isMeasure()
    {
        return m_isMeasure && this.getDataType().isNumeric(); // && this.getDictId()!=null;
    }

    public ColumnRotateHeaderModes getRotateMode()
    {
        return m_rotateMode == null ? ColumnRotateHeaderModes.None : m_rotateMode;
    }

    public String getNotes()
    {
        return m_strNotes;
    }

    public String getReportId()
    {
        return m_strReportId;
    }

    public int getRotateGroupNo()
    {
        return m_nRotateGroupNo < 0 ? -1 : m_nRotateGroupNo;
    }

    public String getTitle()
    {
        return m_strTitle;
    }

    public DataTypes getDataType()
    {
        return m_dataType == null ? DataTypes.STRING : m_dataType;
    }

    public String getMergeTitle()
    {
        return m_strMergeTitle == null || m_strMergeTitle.trim().length() < 1 ? null : m_strMergeTitle;
    }

    public boolean isSummaryable()
    {
        return m_summaryable;
    }

    public ReportColumn setTitle(String title)
    {
        this.m_strTitle = title;
        return this;
    }

    public ReportColumn setRotateGroupNo(int rotateIndex)
    {
        this.m_nRotateGroupNo = rotateIndex;
        return this;
    }

    public ReportColumn setReportId(String reportId)
    {
        this.m_strReportId = reportId;
        return this;
    }

    public ReportColumn setNotes(String notes)
    {
        this.m_strNotes = notes;
        return this;
    }

    public ReportColumn setRotateMode(ColumnRotateHeaderModes mde)
    {
        this.m_rotateMode = mde;
        return this;
    }

    public ReportColumn setMeasure(boolean isMeasure)
    {
        this.m_isMeasure = isMeasure;
        return this;
    }

    public ReportColumn setHide(boolean isHide)
    {
        this.m_isHidden = isHide;
        return this;
    }

    public ReportColumn alignRight()
    {
        this.setHorizonAlign(HorizonAligns.Right);
        return this;
    }

    public ReportColumn alignCenter()
    {
        this.setHorizonAlign(HorizonAligns.Center);
        return this;
    }

    public ReportColumn setHorizonAlign(HorizonAligns HAlign)
    {
        this.m_align = HAlign;
        return this;
    }

    public ReportColumn setDataFormat(String format)
    {
        this.m_strDataFormat = format;
        return this;
    }

    public ReportColumn setFieldName(String fieldName)
    {
        this.m_strFieldName = fieldName;
        return this;
    }

    public ReportColumn setDictId(String dictId)
    {
        this.m_strDictId = dictId;
        return this;
    }

    public ReportColumn setColumnType(ReportColumnTypes c)
    {
        this.m_columnType = c;
        return this;
    }

    public ReportColumn setColor(String color)
    {
        this.m_strColor = color;
        return this;
    }

    public ReportColumn setBgColor(String strcolor)
    {
        this.m_strBgColor = strcolor;
        return this;
    }

    public ReportColumn setColId(int colId)
    {
        this.m_nSortNumber = colId;
        return this;
    }

    public ReportColumn setCalcScript(String calcScript)
    {
        this.m_strCalcScript = calcScript;
        return this;
    }

    public ReportColumn setWidth(int width)
    {
        this.m_nWidth = width;
        return this;
    }

    public ReportColumn setSortMode(ReportSortModes sortMode)
    {
        this.m_sortModes = sortMode;
        return this;
    }

    public ReportColumn setDataType(DataTypes DataType)
    {
        this.m_dataType = DataType;
        return this;
    }

    public ReportColumn setMergeTitle(String s)
    {
        this.m_strMergeTitle = s;
        return this;
    }

    public ReportColumn setSummaryable(boolean canSummary)
    {
        this.m_summaryable = canSummary;
        return this;
    }

    public String toString()
    {
        return this.getTitle();
    }

    public void afterInitializedFromXML(XMLNode nodeItem)
        throws ModelException
    {
        if(nodeItem.valueOf(S_MEASURE.split(",")) == null)
        {
            this.setMeasure(this.getDataType().isNumeric() && this.getDictId() == null);
        }
        if(this.isMeasure())
        {
            if(nodeItem.valueOf(S_ALIGN.split(",")) == null)
            {
                this.setHorizonAlign(HorizonAligns.Right);
            }
            if(null == nodeItem.valueOf(S_SUMMARYABLE.split(",")))
            {
                this.setSummaryable(true);
            }
        }
        ReportAlertCollection alerts = new ReportAlertCollection();
        for(XMLNode node : nodeItem.selectNodes("alert","css","render"))
        {
            ReportAlert a = ModelHelper.getInstance().fromXML(ReportAlert.class,node);
            alerts.add(a);
        }
        for(XMLNode node : nodeItem.selectNodes("link","url"))
        {
            ReportAlert.Link a = ModelHelper.getInstance().fromXML(ReportAlert.Link.class,node);
            alerts.add(a);
        }
        for(XMLNode node : nodeItem.selectNodes("report","ref"))
        {
            ReportAlert.ReportLink a = ModelHelper.getInstance().fromXML(ReportAlert.ReportLink.class,node);
            alerts.add(a);
        }
        /**
         * @todo We should add more section to support more types of alert.
         */
        if(alerts.size() > 0)
        {
            this.m_alerts = alerts;
        }
    }

    public void afterInitializedFromJSON(JSONClass nodeItem)
        throws ModelException
    {
        if(nodeItem.valueOf(S_MEASURE.split(",")) == null)
        {
            this.setMeasure(this.getDataType().isNumeric() && this.getDictId() == null);
        }
        if(this.isMeasure())
        {
            if(nodeItem.valueOf(S_ALIGN.split(",")) == null)
            {
                this.setHorizonAlign(HorizonAligns.Right);
            }
            if(null == nodeItem.valueOf(S_SUMMARYABLE.split(",")))
            {
                this.setSummaryable(true);
            }
        }
        ReportAlertCollection alerts = new ReportAlertCollection();
        JSONArray ary = nodeItem.arrayNode(NODE_ALERTS);
        if(null != ary)
        {
            for(JSONNode jsonnode : ary.items())
            {
                JSONClass alertClass = (JSONClass) jsonnode;
                ReportAlert a = null;
                ReportAlertModels model = ReportAlertModels.fromName(alertClass.valueOf(ReportAlert.NODE_MODEL));
                if(model.equals(ReportAlertModels.Href))
                {
                    a = ModelHelper.getInstance().fromJSON(ReportAlert.Link.class,alertClass);
                }
                else if(model.equals(ReportAlertModels.Report))
                {
                    a = ModelHelper.getInstance().fromJSON(ReportAlert.ReportLink.class,alertClass);
                }
                else
                {
                    a = ModelHelper.getInstance().fromJSON(ReportAlert.class,alertClass);
                }
                alerts.add(a);
                /**
                 * @todo We should add more section to support more types of alert.
                 */
            }
            if(alerts.size() > 0)
            {
                this.m_alerts = alerts;
            }
        }
    }

    public void normalizeModelItem()
    {
        if(Algorithms.isEmpty(this.getFieldName())
           && this.getColumnType().isCalcColumn() == false)
        {
            this.setColumnType(ReportColumnTypes.CalcColumn);
            this.setFieldName("");
            if(this.getTitle() == null)
            {
                this.setTitle("");
            }
        }
        if(Algorithms.isEmpty(this.getFieldName()) == false && this.getFieldName().trim().startsWith("="))
        {
            this.setColumnType(ReportColumnTypes.CalcColumn);
            this.setCalcScript(this.getFieldName());
            this.setFieldName("");
            if(this.getTitle() == null)
            {
                this.setTitle("");
            }
        }
        if(this.getTitle() == null)
        {
            this.setTitle(this.getFieldName());
        }
        for(int n = 0;this.m_alerts != null && n < this.m_alerts.size();n++)
        {
            ReportAlert alert = m_alerts.get(n);
            if(null != alert)
            {
                alert.setGuid(this.getGuid() + "_a" + n);
            }
        }
    }
}
